#include "errorcorrection.h"

#include <cmath>
#include <algorithm>
#include <bitset>

#include <QDebug>

namespace qrcode {

ErrorCorrection::ErrorCorrection()
{
    int p = 1;
    for(int i = 0; i < 256; i++){
        alpha.insert({i, p});
        p *= 2;
        XOR285(p);
    }

    generatorPolynomials.insert({7, {0, 87, 229, 146, 149, 238, 102, 21}});
    generatorPolynomials.insert({8, {0, 175, 238, 208, 249, 215, 252, 196, 28}});
    generatorPolynomials.insert({10, {0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45}});
    generatorPolynomials.insert({13, {0, 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206,
                                      140, 78}});
    generatorPolynomials.insert({14, {0, 199, 249, 155, 48, 190, 124, 218, 137, 216, 87,
                                      207, 59, 22, 91}});
    generatorPolynomials.insert({15, {0, 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140,
                                      124, 5, 99, 105}});
    generatorPolynomials.insert({16, {0, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191,
                                      147, 169, 182, 194, 225, 120}});
    generatorPolynomials.insert({17, {0, 43, 139, 206, 78, 43, 239, 123, 206, 214, 147,
                                      24, 99, 150, 39, 243, 163, 136}});
    generatorPolynomials.insert({18, {0, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187,
                                      152, 148, 252, 179, 5, 98, 96, 153}});
    generatorPolynomials.insert({20, {0, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221,
                                      225, 83, 239, 156, 164, 212, 212, 188, 190}});
    generatorPolynomials.insert({22, {0, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53,
                                      200, 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231}});
    generatorPolynomials.insert({24, {0, 229, 121, 135, 48, 211, 117, 251, 126, 159,
                                      180, 169, 152, 192, 226, 228, 218, 111, 0, 117, 232,
                                      87, 96, 227, 21}});
    generatorPolynomials.insert({26, {0, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111,
                                      28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153,
                                      145, 218, 70}});
    generatorPolynomials.insert({28, {0, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195,
                                      147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212,
                                      119, 242, 37, 9, 123}});
    generatorPolynomials.insert({30, {0, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110,
                                      86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156,
                                      37, 251, 216, 238, 40, 192, 180}});
}


group ErrorCorrection::Ecoding(group &codeGroup, block_int &poly)
{
    group eccGroup;
    if(codeGroup.size() == 0){
        return eccGroup;
    }

    block t_block;
    for(int i = 0; i < codeGroup.size(); ++i) {
        t_block = codeGroup.at(i);
        eccGroup.push_back(GeneratingECC(ToblockInt(t_block), poly));
    }
    return eccGroup;
}

block ErrorCorrection::GeneratingECC(block_int msgPoly, block_int genPoly)
{
    int numDataCodewords = msgPoly.size();
    int numGeneratorEEC = genPoly.size();
    for(int i = 1; i < numGeneratorEEC; ++i){
        msgPoly.push_back(0);
    }

    /**
     * 用生成多项式除数据多项式 步骤：
     *   1）.生成多项式各项乘以数据多项式最高项系数，使其最高项系数与数据多项式最高项相同。
     *   2）.将1）的结果与数据多项式异或。
     *   3）.重复上述步骤n次，n为数据码字个数。
     *
     * 注意：
     *   -上述 1）中的生成多项式的乘法需在伽罗华域GF(256)中进行。
     */
    block_int _genePoly;
    int _lead;
    for(int i = 0; i < numDataCodewords; ++i){
        _genePoly = genPoly;
        _lead = msgPoly.at(0);
        IntToBase2exponent(_lead);
        //1.生成多项式乘以数据多项式最高项系数
        MultiplyLeadTerm(_genePoly, _lead);
        Base2exponentToBlock_int(_genePoly);
        //2.数据多项式与生成多项式异或，得到余数
        PolyXOR(msgPoly, _genePoly);
    }

//    for(int j = 0; j < msgPoly.size(); j++){
//        qDebug() << msgPoly.at(j);
//    }

    //将block_int转换为block,返回
    return ToBlock(msgPoly);
}

bool ErrorCorrection::IsCorrect(std::vector<std::vector<int> > _block, std::vector<std::vector<int> > _ecb)
{

}

block_int ErrorCorrection::GetGeneratorPolynomials(int ECCodewordsPerBlock)
{
    auto posKey = generatorPolynomials.find(ECCodewordsPerBlock);
    if(posKey != generatorPolynomials.end()){
        return posKey->second;
    } else {
        qDebug() << "errorcorrection.cpp: 找不到生成多项式";
    }
}

void ErrorCorrection::XOR285(int &x)
{
    if(x > 255){
        x = x^285;
    }
}

int ErrorCorrection::ToInt(codeword &c)
{
    int _int = 0;
    int tmp;
    char t_ch;
    for(int i = 0; i < c.size(); ++i){
        t_ch = c.at(i);
        if(t_ch == '1'){
            tmp = 1;
        } else if(t_ch == '0'){
            tmp = 0;
        } else {
            qDebug() << "errorcorrection.cpp: ToInt()错误";
        }
        tmp *= (int)pow(2, (c.size()-i-1));
        _int += tmp;
    }
    return _int;
}

std::vector<char> ErrorCorrection::ToBinaryV(int &x)
{
    std::bitset<8> bits = x;
    std::string str = bits.to_string();
    std::vector<char> vec;
    for(int i = 0; i < str.length(); i++){
        vec.push_back(str.at(i));
    }
    return vec;
}

block_int ErrorCorrection::ToblockInt(block &b)
{
    block_int _block_int;
    codeword tmp;
    for(int i = 0; i < b.size(); ++i){
        tmp = b.at(i);
        _block_int.push_back(ToInt(tmp));
    }
    return _block_int;
}

block ErrorCorrection::ToBlock(block_int &b)
{
    block _block;
    std::vector<char> tmp;
    for(int i = 0; i < b.size(); ++i){
        _block.push_back(ToBinaryV(b.at(i)));
    }
    return _block;
}

void ErrorCorrection::Base2exponentToBlock_int(block_int &b)
{
    for(auto &i : b){
        Base2exponentToInt(i);
    }
}

void ErrorCorrection::Base2exponentToInt(int &x)
{
    auto poskey = alpha.find(x);
    if(poskey != alpha.end()) {
        x = poskey->second;
    } else {
        qDebug() << "errorcorrection.cpp: Base2exponentToInt 转换失败";
    }
}

void ErrorCorrection::IntToBase2exponent(int &x)
{  
    bool f = true;
    std::map<int, int>::iterator posVal;
    for(posVal = alpha.begin(); posVal != alpha.end(); posVal++){
        if(posVal->second == x){
            x = posVal->first;
            f = false;
            //qDebug() << posVal->first << ", " << posVal->second;
            break;
        }
    }
    if(f){
        qDebug() << "errorcorrection.cpp: IntToBase2exponent 转换失败";
    }
}

void ErrorCorrection::MultiplyLeadTerm(block_int &b, int x)
{
    for(auto &i : b){
        i = (i + x) % 255;
    }
}

void ErrorCorrection::PolyXOR(block_int &b, block_int &b1)
{
    block_int::iterator bpos;
    block_int::iterator b1pos;
    for(bpos = b.begin(), b1pos = b1.begin(); b1pos != b1.end(); ++b1pos, ++bpos){
        *bpos = *bpos ^ *b1pos;
    }
    //去掉最高项
    b.erase(b.begin());
}

}
